// 2024/7/26: 首个版本
// 2024/8/15: 支持GPUDecoderTestResults转换
// 2024/8/23: 支持ThreeStrings转换
// 2024/9/26: 新增CallNativeFunctionInput转换
// 2024/9/27: 支持NativeClassInfo.library_debug_ids
// 2024/10/22: 修正部分转换异常
// 2024/12/5: 实现DataReferenceInfo转换
// 2024/12/26: 修正StringAndBool转换
// 2025/1/14: 修正DataReferenceInfo转换
// 2025/4/21: 输出BusMessageInfo包含协议文件ID
// 2025/5/22: 支持GenerateBusMessageInput和BusMessage转换

using System;
using System.Collections.Generic;
using System.Linq;
using Google.Protobuf;

namespace AgencyTypes
{
    static class AgencyTypesConversion
    {
        public static TimeWithSession ToPB(this ASEva.TimeWithSession input)
        {
            var output = new TimeWithSession();
            output.Time = input.Time;
            output.Session = input.Session.ToString();
            return output;
        }

        public static (String, String) ToCommon(this TwoStrings input)
        {
            return (input.HasItem1 ? input.Item1 : null,
                input.HasItem2 ? input.Item2 : null);
        }

        public static DequeueDataFromNativeOutput ToPB(this byte[][] input)
        {
            var output = new DequeueDataFromNativeOutput();
            foreach (var bytes in input)
            {
                if (bytes == null) continue;
                output.DataArray.Add(ByteString.CopyFrom(bytes));
            }
            return output;
        }

        public static (String, String, byte[]) ToCommon(this EnqueueDataToNativeInput input)
        {
            return (input.HasNativeClassId ? input.NativeClassId : null,
                input.HasDataId ? input.DataId : null,
                input.HasData ? input.Data.ToByteArray() : null);
        }

        public static StringArray ToPB(this String[] input)
        {
            var output = new StringArray();
            foreach (var str in input)
            {
                if (str == null) continue;
                output.StringArray_.Add(str);
            }
            return output;
        }

        public static GetAllDeviceStatusOutput ToPB(this Dictionary<String, ASEva.DeviceStatusDetail> input)
        {
            var output = new GetAllDeviceStatusOutput();
            foreach (var pair in input)
            {
                var detail = new DeviceStatusDetail();
                detail.Status = pair.Value.Status.ToString();
                if (pair.Value.Description != null) detail.Description = pair.Value.Description;
                output.DetailMap[pair.Key] = detail;
            }
            return output;
        }

        public static StringDoubleMap ToPB(this Dictionary<String, double> input)
        {
            var output = new StringDoubleMap();
            foreach (var pair in input)
            {
                output.NumberMap[pair.Key] = pair.Value;
            }
            return output;
        }

        public static uint? ToCommon(this OptionalUInt input)
        {
            return input.HasValue ? input.Value : null;
        }

        public static GetChannelStatusOutput ToPB(this (bool, double[], double[]) input)
        {
            var output = new GetChannelStatusOutput();
            output.Item1 = input.Item1;
            if (input.Item2 != null) output.Item2.AddRange(input.Item2);
            if (input.Item3 != null) output.Item3.AddRange(input.Item3);
            return output;
        }

        public static GetAudioDriversOutput ToPB(this ASEva.AudioDriverInfo[] input)
        {
            var output = new GetAudioDriversOutput();
            foreach (var inputInfo in input)
            {
                if (inputInfo.DriverID == null) continue;
                var outputInfo = new AudioDriverInfo();
                outputInfo.DriverId = inputInfo.DriverID;
                if (inputInfo.DriverName != null) outputInfo.DriverName = inputInfo.DriverName;
                output.Infos.Add(outputInfo);
            }
            return output;
        }

        public static GetAudioDevicesOutput ToPB(this ASEva.AudioDeviceInfo[] input)
        {
            var output = new GetAudioDevicesOutput();
            foreach (var inputInfo in input)
            {
                if (inputInfo.DeviceID == null) continue;
                var outputInfo = new AudioDeviceInfo();
                outputInfo.DeviceId = inputInfo.DeviceID;
                if (inputInfo.DeviceName != null) outputInfo.DeviceName = inputInfo.DeviceName;
                output.Infos.Add(outputInfo);
            }
            return output;
        }

        public static IntArray ToPB(this int[] input)
        {
            var output = new IntArray();
            output.IntArray_.AddRange(input);
            return output;
        }

        public static TwoNumber ToPB(this ASEva.BufferRange input)
        {
            var output = new TwoNumber();
            output.Item1 = input.begin;
            output.Item2 = input.end;
            return output;
        }

        public static GetBusChannelsInfoOutput ToPB(this ASEva.BusChannelInfo[] input)
        {
            var output = new GetBusChannelsInfoOutput();
            foreach (var inputInfo in input)
            {
                if(inputInfo == null) continue;
                var outputInfo = new BusChannelInfo();
                outputInfo.Type = inputInfo.Type.ToString();
                output.Infos.Add(outputInfo);
            }
            return output;
        }

        public static (int, uint?) ToCommon(this GetChannelStatusInput input)
        {
            return (input.Channel, input.HasToleranceMillisecond ? input.ToleranceMillisecond : null);
        }

        public static (String, uint?) ToCommon(this StringAndOptionalUInt input)
        {
            return (input.Item1, input.HasItem2 ? input.Item2 : null);
        }

        public static GetBusDevicesOutput ToPB(this Dictionary<ASEva.BusDeviceID, ASEva.BusDeviceInfo> input)
        {
            var output = new GetBusDevicesOutput();
            foreach (var inputPair in input)
            {
                if (inputPair.Key.Type == null || inputPair.Value.SupportedTypes == null) continue;
                var outputPair = new GetBusDevicesOutput.Types.Pair();
                outputPair.Key = new BusDeviceID
                {
                    Type = inputPair.Key.Type,
                    Serial = inputPair.Key.Serial,
                    Index = inputPair.Key.Index
                };
                outputPair.Value = new BusDeviceInfo();
                foreach (var type in inputPair.Value.SupportedTypes)
                {
                    outputPair.Value.SupportedTypes.Add(type.ToString());
                }
                if (inputPair.Value.Description != null) outputPair.Value.Description = inputPair.Value.Description;
                output.InfoMap.Add(outputPair);
            }
            return output;
        }

        public static (int, uint) ToCommon(this IntAndUInt input)
        {
            return (input.Item1, input.Item2);
        }

        public static BusMessageInfo ToPB(this ASEva.BusMessageInfo input)
        {
            var output = new BusMessageInfo
            {
                MessageId = input.MessageID ?? "",
                MessageName = input.MessageName ?? "",
                LocalId = input.LocalID,
                ByteLength = input.ByteLength,
                ProtocolFileId = input.OwnerFile?.FileID ?? "",
            };
            return output;
        }

        public static OptionalDouble ToPB(this double? input)
        {
            var output = new OptionalDouble();
            if (input != null) output.Value = input.Value;
            return output;
        }

        public static OptionalInt ToPB(this int? input)
        {
            var output = new OptionalInt();
            if (input != null) output.Value = input.Value;
            return output;
        }

        public static GetBusProtocolFileIDListOutput ToPB(this ASEva.BusProtocolFileID[] input)
        {
            var output = new GetBusProtocolFileIDListOutput();
            foreach (var inputID in input)
            {
                if (inputID.FileName == null || inputID.MD5 == null) continue;
                var outputID = new BusProtocolFileID();
                outputID.FileName = inputID.FileName;
                outputID.Md5 = inputID.MD5;
                output.List.Add(outputID);
            }
            return output;
        }

        public static ASEva.BusProtocolFileID ToCommon(this BusProtocolFileID input)
        {
            return new ASEva.BusProtocolFileID
            {
                FileName = input.FileName,
                MD5 = input.Md5
            };
        }

        public static BusSignalInfo ToPB(this ASEva.BusSignalInfo input)
        {
            var output = new BusSignalInfo
            {
                SignalId = input.SignalID ?? "",
                SignalName = input.SignalName ?? "",
            };
            if (input.Unit != null) output.Unit = input.Unit;
            if (input.Enums != null)
            {
                foreach (var pair in input.Enums)
                {
                    output.Enums[pair.Key] = pair.Value;
                }
            }
            return output;
        }

        public static StringStringMap ToPB(this Dictionary<String, String> input)
        {
            var output = new StringStringMap();
            foreach (var pair in input)
            {
                output.StringMap[pair.Key] = pair.Value;
            }
            return output;
        }

        public static Timestamp ToPB(this ASEva.Timestamp input)
        {
            var output = new Timestamp
            {
                Session = input.Session.ToString(),
                Offset = input.Offset,
                OffsetSync = input.OffsetSync.ToString(),
            };
            if (input.TimeInfo != null)
            {
                output.TimeInfo = new IndependentTimeInfo
                {
                    CpuTick = input.TimeInfo.CPUTick,
                    HostPosix = input.TimeInfo.HostPosix,
                    GuestPosix = input.TimeInfo.GuestPosix,
                    GnssPosix = input.TimeInfo.GNSSPosix,
                };
            }
            return output;
        }

        public static GetChannelLatestTimestampsOutput ToPB(this ASEva.Timestamp[] input)
        {
            var output = new GetChannelLatestTimestampsOutput();
            foreach (var ts in input)
            {
                output.Timestamps.Add(ts.ToPB());
            }
            return output;
        }

        public static StringBoolMap ToPB(this Dictionary<String, bool> input)
        {
            var output = new StringBoolMap();
            foreach (var pair in input)
            {
                output.BoolMap[pair.Key] = pair.Value;
            }
            return output;
        }

        public static StringStringMap ToPB<T>(this Dictionary<String, T> input) where T : struct, Enum
        {
            var output = new StringStringMap();
            foreach (var pair in input)
            {
                output.StringMap[pair.Key] = pair.Value.ToString();
            }
            return output;
        }

        public static StringArray ToPB<T>(this T[] input) where T : struct, Enum
        {
            var output = new StringArray();
            foreach (var e in input)
            {
                output.StringArray_.Add(e.ToString());
            }
            return output;
        }

        public static ConsoleClassInfo ToPB(this ASEva.ConsoleClassInfo input)
        {
            return new ConsoleClassInfo
            {
                Id = input.ID ?? "",
                Title = input.Title ?? "",
                OwnerPluginId = input.OwnerPluginID ?? "",
            };
        }

        public static GetConsoleClassTableOutput ToPB(this Dictionary<String, ASEva.ConsoleClassInfo> input)
        {
            var output = new GetConsoleClassTableOutput();
            foreach (var pair in input)
            {
                output.InfoMap[pair.Key] = pair.Value.ToPB();
            }
            return output;
        }

        public static GetRelatedModulesConfigStatusOutput ToPB(this (ASEva.ConfigStatus, ASEva.ConfigStatus[]) input)
        {
            var output = new GetRelatedModulesConfigStatusOutput();
            output.Item1 = input.Item1.ToString();
            if (input.Item2 != null)
            {
                foreach (var s in input.Item2) output.Item2.Add(s.ToString());
            }
            return output;
        }

        public static CPUTimeModel ToPB(this ASEva.CPUTimeModel input)
        {
            return new CPUTimeModel
            {
                StartCpuTick = input.StartCPUTick,
                CpuTicksPerSecond = input.CPUTicksPerSecond,
            };
        }

        public static GetDeviceClassTableOutput ToPB(this Dictionary<String, ASEva.DeviceClassInfo> input)
        {
            var output = new GetDeviceClassTableOutput();
            foreach (var pair in input)
            {
                output.InfoMap[pair.Key] = new DeviceClassInfo
                {
                    Id = pair.Value.ID ?? "",
                    Title = pair.Value.Title ?? "",
                    OwnerPluginId = pair.Value.OwnerPluginID ?? "",
                };
            }
            return output;
        }

        public static StringArray ToPB(this ASEva.SessionIdentifier[] input)
        {
            var output = new StringArray();
            foreach (var i in input)
            {
                output.StringArray_.Add(i.ToString());
            }
            return output;
        }

        public static EventInfo ToPB(this ASEva.EventInfo input)
        {
            var eventHandle = input.Data.EventHandle as String;
            var output = new EventInfo
            {
                Data = new EventData
                {
                    Name = input.Data.Name ?? "",
                    Session = input.Data.Session.ToString(),
                    TimestampLocal = input.Data.TimeStampLocal.ToString("yyyy-MM-dd-HH-mm-ss-fff"),
                    ConfigId = input.Data.ConfigID ?? "",
                    Description = input.Data.Description ?? "",
                    EventHandle = eventHandle ?? Guid.NewGuid().ToString(),
                },
                Comment = input.Comment ?? "",
                SessionRecordStatus = input.SessionRecordStatus.ToString(),
            };
            if (input.Data.Offset != null) output.Data.Offset = input.Data.Offset.Value;
            return output;
        }

        public static PosixTimeModel ToPB(this ASEva.PosixTimeModel input)
        {
            return new PosixTimeModel
            {
                StartPosix = input.StartPosix,
                TimeRatio = input.TimeRatio,
            };
        }

        public static (ASEva.SessionIdentifier, int) ToCommon(this GetGraphDataInput input)
        {
            return (ASEva.SessionIdentifier.FromString(input.Session), input.GraphId);
        }

        public static GraphData ToPB(this ASEva.GraphData input)
        {
            var output = new GraphData
            {
                Id = input.ID,
                Definition = new GraphDefinition
                {
                    Type = input.Definition.Type.ToString(),
                    MainTitle = input.Definition.MainTitle ?? "",
                },
                DataDim0 = input.Data?.GetLength(0) ?? 0,
                DataDim1 = input.Data?.GetLength(1) ?? 0,
            };
            if (input.Definition.Config != null) output.Definition.Config.AddRange(input.Definition.Config);
            if (input.Definition.ColumnTitles != null) output.Definition.ColumnTitles.AddRange(input.Definition.ColumnTitles);
            if (input.Params != null) output.Params.AddRange(input.Params);
            if (input.Data != null)
            {
                var tmp = new double[input.Data.GetLength(0) * input.Data.GetLength(1)];    
                Buffer.BlockCopy(input.Data, 0, tmp, 0, tmp.Length * sizeof(double));
                output.Data.AddRange(tmp);
            }
            return output;
        }

        public static GetGraphicCardInfosOutput ToPB(this ASEva.GraphicCardInfo[] input)
        {
            var output = new GetGraphicCardInfosOutput();
            foreach (var info in input)
            {
                output.Infos.Add(new GraphicCardInfo
                {
                    Vendor = info.Vendor.ToString(),
                    CardIndex = info.CardIndex,
                    MemoryCapacity = info.MemoryCapacity,
                    MemoryFree = info.MemoryFree, 
                });
            }
            return output;
        }

        public static (ASEva.SessionIdentifier, double, bool) ToCommon(this GetDateTimeInput input)
        {
            return (ASEva.SessionIdentifier.FromString(input.Session), input.TimeOffset, input.UseGnss);
        }

        public static GetLogMessagesOutput ToPB(this ASEva.LogMessage[] input)
        {
            var output = new GetLogMessagesOutput();
            foreach (var msg in input)
            {
                if (msg.Text == null) continue;
                output.Messages.Add(new LogMessage
                {
                    Type = msg.Type.ToString(),
                    Text = msg.Text,
                    Time = msg.Time.ToString("yyyy-MM-dd-HH-mm-ss-fff"),
                    RepeatedCount = msg.RepeatedCount,
                });
            }
            return output;
        }

        public static GetModuleChildConfigStatusOutput ToPB(this ASEva.ConfigStatus[] input)
        {
            var output = new GetModuleChildConfigStatusOutput();
            foreach (var child in input)
            {
                output.ChildrenStatus.Add(child.ToString());
            }
            return output;
        }

        public static GetModuleConfigStatusOutput ToPB(this (ASEva.ConfigStatus, String) input)
        {
            var output = new GetModuleConfigStatusOutput();
            output.Item1 = input.Item1.ToString();
            if (input.Item2 != null) output.Item2 = input.Item2;
            return output;
        }

        public static ModuleDetails ToPB(this ASEva.ModuleDetails input)
        {
            var output = new ModuleDetails();
            if (input.OutputSamples != null)
            {
                foreach (var pair in input.OutputSamples)
                {
                    output.OutputSamples[pair.Key] = pair.Value;
                }
            }
            if (input.OutputScenes != null)
            {
                output.OutputScenes.AddRange(input.OutputScenes);
            }
            if (input.OutputSignals != null)
            {
                foreach (var pair in input.OutputSignals)
                {
                    var names = new StringArray();
                    names.StringArray_.AddRange(pair.Value);
                    output.OutputSignals[pair.Key] = names;
                }
            }
            if (input.OutputGraphs != null)
            {
                output.OutputGraphs.AddRange(input.OutputGraphs);
            }
            if (input.RequiredSignalPackings != null)
            {
                output.RequiredSignalPackings.AddRange(input.RequiredSignalPackings);
            }
            if (input.RequiredVideoChannels != null)
            {
                foreach (var e in input.RequiredVideoChannels)
                {
                    output.RequiredVideoChannels.Add(e.ToString());
                }
            }
            if (input.RequiredRecordDataTypes != null)
            {
                foreach (var e in input.RequiredRecordDataTypes)
                {
                    output.RequiredRecordDataTypes.Add(e.ToString());
                }
            }
            return output;
        }

        public static GetNativeClassTableOutput ToPB(this Dictionary<String, ASEva.NativeClassInfo> input)
        {
            var output = new GetNativeClassTableOutput();
            foreach (var pair in input)
            {
                var info = new NativeClassInfo
                {
                    Id = pair.Value.ID ?? "",
                    Title = pair.Value.Title ?? "",
                    OwnerPluginId = pair.Value.OwnerPluginID ?? "",
                    NativeType = pair.Value.NativeType ?? "",
                };
                if (pair.Value.LibraryVersions != null)
                {
                    foreach (var libPair in pair.Value.LibraryVersions)
                    {
                        info.LibraryVersions[libPair.Key.ToString()] = libPair.Value.ToString();
                    }
                }
                if (pair.Value.LibraryDebugIDs != null)
                {
                    foreach (var idPair in pair.Value.LibraryDebugIDs)
                    {
                        info.LibraryDebugIds[idPair.Key.ToString()] = idPair.Value;
                    }
                }
                output.InfoMap[pair.Key] = info;
            }
            return output;
        }

        public static StringStringMap ToPB(this Dictionary<String, Version> input)
        {
            var output = new StringStringMap();
            foreach (var pair in input)
            {
                output.StringMap[pair.Key] = pair.Value.ToString();
            }
            return output;
        }

        public static PluginPackInfo ToPB(this ASEva.PluginPackInfo input)
        {
            return new PluginPackInfo
            {
                Id = input.ID ?? "",
                Name = input.Name ?? "",
                Version = input.Version.ToString(),
                Brief = input.Brief ?? "",
                Status = input.Status.ToString(),
                Error = input.Error.ToString(),
                AppLayerDetails = input.AppLayerDetails ?? "",
                NativeLayerDetails = input.NativeLayerDetails ?? "",
            };
        }

        public static GetPluginThirdPartyNoticesOutput ToPB(this Dictionary<String, Dictionary<String, String>> input)
        {
            var output = new GetPluginThirdPartyNoticesOutput();
            foreach (var pair in input)
            {
                if (pair.Value == null) continue;
                var map = new StringStringMap();
                foreach (var subPair in pair.Value)
                {
                    if (subPair.Value == null) continue;
                    map.StringMap[subPair.Key] = subPair.Value;
                }
                output.Notices[pair.Key] = map;
            }
            return output;
        }

        public static (int, double, double) ToCommon(this GetPreviewJpegInput input)
        {
            return (input.Channel, input.Timeline, input.MaxGap);
        }

        public static CameraInfo ToPB(this ASEva.Samples.CameraInfo input)
        {
            var output = new CameraInfo();
            if (input.HorizontalFov != null) output.HorizontalFov = input.HorizontalFov.Value;
            output.SpecialCameraType = input.SpecialCameraType.ToString();

            String metaProtocol = null;
            double[] meta = null;
            switch (input.SpecialCameraType)
            {
                case ASEva.Samples.SpecialCameraType.Normal:
                    metaProtocol = "default-v2";
                    meta = new double[19];
                    {
                        var target = input.SpecialCameraInfo as ASEva.Samples.DefaultCameraMeta;
                        meta[0] = target.Common.Inverted ? 1 : 0;
                        meta[1] = target.Common.VFlipped ? 1 : 0;
                        meta[2] = target.Common.Undistorted ? 1 : 0;
                        meta[3] = target.Common.UndistortedHFOV;
                        meta[4] = target.Common.AR;
                        meta[5] = target.Common.CU;
                        meta[6] = target.Common.CV;
                        meta[7] = target.Common.K1;
                        meta[8] = target.Common.K2;
                        meta[9] = target.Common.P1;
                        meta[10] = target.Common.P2;
                        meta[11] = target.Common.K3;
                        meta[12] = target.Common.K4;
                        meta[13] = target.Common.K5;
                        meta[14] = target.Common.K6;
                    }
                    break;
                case ASEva.Samples.SpecialCameraType.FrontCamera:
                case ASEva.Samples.SpecialCameraType.AnyCamera:
                    metaProtocol = "generic-v2";
                    meta = new double[32];
                    {
                        var target = input.SpecialCameraInfo as ASEva.Samples.GenericCameraMeta;
                        meta[0] = target.Common.Inverted ? 1 : 0;
                        meta[1] = target.Common.VFlipped ? 1 : 0;
                        meta[2] = target.Common.Undistorted ? 1 : 0;
                        meta[3] = target.YawRectified ? 1 : 0;
                        meta[4] = target.PitchRectified ? 1 : 0;
                        meta[5] = target.RollRectified ? 1 : 0;
                        meta[6] = target.Common.UndistortedHFOV;
                        meta[7] = target.Common.AR;
                        meta[8] = target.Common.CU;
                        meta[9] = target.Common.CV;
                        meta[10] = target.Common.K1;
                        meta[11] = target.Common.K2;
                        meta[12] = target.Common.P1;
                        meta[13] = target.Common.P2;
                        meta[14] = target.PositionX;
                        meta[15] = target.PositionY;
                        meta[16] = target.PositionZ;
                        meta[17] = target.OriginYaw;
                        meta[18] = target.OriginPitch;
                        meta[19] = target.OriginRoll;
                        meta[20] = target.RectifiedYaw;
                        meta[21] = input.SpecialCameraType == ASEva.Samples.SpecialCameraType.FrontCamera ? 2 : 1;
                        meta[22] = target.ShakeYaw;
                        meta[23] = target.ShakePitch;
                        meta[24] = target.Common.K3;
                        meta[25] = target.Common.K4;
                        meta[26] = target.Common.K5;
                        meta[27] = target.Common.K6;
                    }
                    break;
                case ASEva.Samples.SpecialCameraType.LeftLaneCamera:
                case ASEva.Samples.SpecialCameraType.RightLaneCamera:
                    metaProtocol = "line-v3";
                    meta = new double[26];
                    {
                        var target = input.SpecialCameraInfo as ASEva.Samples.LaneLineCameraMeta;
                        meta[0] = target.Common.Inverted ? 1 : 0;
                        meta[1] = target.Common.VFlipped ? 1 : 0;
                        meta[2] = target.Common.Undistorted ? 1 : 0;
                        meta[3] = target.Common.UndistortedHFOV;
                        meta[4] = target.Common.AR;
                        meta[5] = target.Common.CU;
                        meta[6] = target.Common.CV;
                        meta[7] = target.Common.K1;
                        meta[8] = target.Common.K2;
                        meta[9] = target.Common.P1;
                        meta[10] = target.Common.P2;
                        meta[11] = target.ZeroPosition.X;
                        meta[12] = target.ZeroPosition.Y;
                        meta[13] = target.MeterPosition.X;
                        meta[14] = target.MeterPosition.Y;
                        meta[15] = target.FiftyRatio;
                        meta[16] = target.MeterScale;
                        meta[17] = target.IsRightSide ? 1 : 0;
                        meta[18] = target.Common.K3;
                        meta[19] = target.Common.K4;
                        meta[20] = target.Common.K5;
                        meta[21] = target.Common.K6;
                    }
                    break;
                case ASEva.Samples.SpecialCameraType.LeftBlindSpotCamera:
                case ASEva.Samples.SpecialCameraType.RightBlindSpotCamera:
                    metaProtocol = "bs-v3";
                    meta = new double[32];
                    {
                        var target = input.SpecialCameraInfo as ASEva.Samples.BlindSpotCameraMeta;
                        meta[0] = target.Common.Inverted ? 1 : 0;
                        meta[1] = target.Common.VFlipped ? 1 : 0;
                        meta[2] = target.Common.Undistorted ? 1 : 0;
                        meta[3] = target.Common.UndistortedHFOV;
                        meta[4] = target.Common.AR;
                        meta[5] = target.Common.CU;
                        meta[6] = target.Common.CV;
                        meta[7] = target.Common.K1;
                        meta[8] = target.Common.K2;
                        meta[9] = target.Common.P1;
                        meta[10] = target.Common.P2;
                        meta[11] = target.P00Position.X;
                        meta[12] = target.P00Position.Y;
                        meta[13] = target.P30Position.X;
                        meta[14] = target.P30Position.Y;
                        meta[15] = target.P04Position.X;
                        meta[16] = target.P04Position.Y;
                        meta[17] = target.P34Position.X;
                        meta[18] = target.P34Position.Y;
                        meta[19] = target.P0HRatio;
                        meta[20] = target.P3HRatio;
                        meta[21] = target.PH0Ratio;
                        meta[22] = target.PH4Ratio;
                        meta[23] = target.IsRightSide ? 1 : 0;
                        meta[24] = target.Common.K3;
                        meta[25] = target.Common.K4;
                        meta[26] = target.Common.K5;
                        meta[27] = target.Common.K6;
                    }
                    break;
                case ASEva.Samples.SpecialCameraType.NormalFisheyeCamera:
                case ASEva.Samples.SpecialCameraType.FrontFisheyeCamera:
                case ASEva.Samples.SpecialCameraType.AnyFisheyeCamera:
                    metaProtocol = "fisheye-v1";
                    meta = new double[26];
                    {
                        var target = input.SpecialCameraInfo as ASEva.Samples.FisheyeCameraMeta;
                        meta[0] = target.Inverted ? 1 : 0;
                        meta[1] = target.VFlipped ? 1 : 0;
                        meta[2] = target.Undistorted ? 1 : 0;
                        meta[3] = target.UndistortedHFOV;
                        meta[4] = target.HFOVFisheye;
                        meta[5] = target.AR;
                        meta[6] = target.CU;
                        meta[7] = target.CV;
                        meta[8] = target.K1;
                        meta[9] = target.K2;
                        meta[10] = target.K3;
                        meta[11] = target.K4;
                        meta[12] = target.HasExtrinsicsInfo ? 1 : 0;
                        if (target.HasExtrinsicsInfo)
                        {
                            meta[13] = target.YawRectified ? 1 : 0;
                            meta[14] = target.PitchRectified ? 1 : 0;
                            meta[15] = target.RollRectified ? 1 : 0;
                            meta[16] = target.PositionX;
                            meta[17] = target.PositionY;
                            meta[18] = target.PositionZ;
                            meta[19] = target.OriginYaw;
                            meta[20] = target.OriginPitch;
                            meta[21] = target.OriginRoll;
                            meta[22] = target.RectifiedYaw;
                            meta[23] = input.SpecialCameraType == ASEva.Samples.SpecialCameraType.FrontFisheyeCamera ? 2 : 1;
                            meta[24] = target.ShakeYaw;
                            meta[25] = target.ShakePitch;
                        }
                    }
                    break;
                case ASEva.Samples.SpecialCameraType.CustomizedCamera:
                    metaProtocol = "customized-v1";
                    meta = new double[20];
                    {
                        var target = input.SpecialCameraInfo as ASEva.Samples.CustomizedCameraMeta;
                        meta[0] = target.Inverted ? 1 : 0;
                        meta[1] = target.VFlipped ? 1 : 0;
                        meta[2] = target.Undistorted ? 1 : 0;
                        meta[3] = target.YawRectified ? 1 : 0;
                        meta[4] = target.PitchRectified ? 1 : 0;
                        meta[5] = target.RollRectified ? 1 : 0;
                        meta[6] = target.UndistortedHFOV;
                        meta[9] = target.PositionX;
                        meta[10] = target.PositionY;
                        meta[11] = target.PositionZ;
                        meta[12] = target.OriginYaw;
                        meta[13] = target.OriginPitch;
                        meta[14] = target.OriginRoll;
                        meta[15] = target.RectifiedYaw;
                    }
                    break;
                default:
                    metaProtocol = "";
                    meta = new double[0];
                    break;
            }

            output.MetaProtocol = metaProtocol;
            output.Meta.AddRange(meta);
            return output;
        }

        public static GetPreviewJpegOutput ToPB(this (byte[], ASEva.Timestamp?, ASEva.Samples.CameraInfo) input)
        {
            var output = new GetPreviewJpegOutput();
            if (input.Item1 != null) output.Item1 = ByteString.CopyFrom(input.Item1);
            if (input.Item2 != null) output.Item2 = input.Item2.Value.ToPB();
            if (input.Item3 != null) output.Item3 = input.Item3.ToPB();
            return output;
        }

        public static GetProcessorClassTableOutput ToPB(this Dictionary<String, ASEva.ProcessorClassInfo> input)
        {
            var output = new GetProcessorClassTableOutput();
            foreach (var pair in input)
            {
                output.InfoMap[pair.Key] = new ProcessorClassInfo
                {
                    Id = pair.Value.ID ?? "",
                    Title = pair.Value.Title ?? "",
                    OwnerPluginId = pair.Value.OwnerPluginID ?? "",
                };
            }
            return output;
        }

        public static StringStringMap ToPB(this Dictionary<String, ASEva.SceneTitle> input)
        {
            var output = new StringStringMap();
            foreach (var pair in input)
            {
                if (pair.Value?.Titles == null) continue;
                output.StringMap[pair.Key] = String.Join(',', pair.Value.Titles);
            }
            return output;
        }

        public static GetSessionFilterTableOutput ToPB(this Dictionary<ASEva.SessionIdentifier, ASEva.SessionFilterFlags> input)
        {
            var output = new GetSessionFilterTableOutput();
            foreach (var pair in input)
            {
                output.FlagsMap[pair.Key.ToString()] = new SessionFilterFlags
                {
                    SearchTrue = pair.Value.SearchTrue,
                    CheckTrue = pair.Value.CheckTrue,
                };
            }
            return output;
        }

        public static (String, bool) ToCommon(this StringAndBool input)
        {
            return (input.Item1, input.Item2);
        }

        public static SignalTreeNode ToPB(this ASEva.SignalTreeNode input)
        {
            var output = new SignalTreeNode();
            output.Type = input.Type.ToString();
            output.Id = input.ID ?? "";
            output.Name = input.Name ?? "";
            if (input.Children != null)
            {
                foreach (var child in input.Children)
                {
                    if (child == null) continue;
                    output.Children.Add(child.ToPB());
                }
            }
            return output;
        }

        public static GetSignalTreeOutput ToPB(this ASEva.SignalTreeNode[] input)
        {
            var output = new GetSignalTreeOutput();
            foreach (var node in input)
            {
                if (node == null) continue;
                output.RootNodes.Add(node.ToPB());
            }
            return output;
        }

        public static TaskClassInfo ToPB(this ASEva.TaskClassInfo input)
        {
            var output = new TaskClassInfo
            {
                Id = input.ID ?? "",
                Title = input.Title ?? "",
                OwnerPluginId = input.OwnerPluginID ?? "",
            };
            if (input.DefaultConfig != null) output.DefaultConfig = input.DefaultConfig;
            return output;
        }

        public static GetTaskClassTableOutput ToPB(this Dictionary<String, ASEva.TaskClassInfo> input)
        {
            var output = new GetTaskClassTableOutput();
            foreach (var pair in input)
            {
                output.InfoMap[pair.Key] = pair.Value.ToPB();
            }
            return output;
        }

        public static IntSize ToPB(this ASEva.IntSize input)
        {
            return new IntSize
            {
                Width = input.Width,
                Height = input.Height
            };
        }

        public static VideoInputMode ToPB(this ASEva.VideoInputMode input)
        {
            return new VideoInputMode
            {
                InputCodec = input.InputCodec.ToString(),
                Size = input.Size.ToPB(),
            };
        }

        public static GetVideoChannelsInfoOutput ToPB(this ASEva.VideoChannelInfo[] input)
        {
            var output = new GetVideoChannelsInfoOutput();
            foreach (var info in input)
            {
                output.Infos.Add(new VideoChannelInfo
                {
                    InputMode = info.InputMode.ToPB(),
                    RecordFps = info.RecordFPS,
                    RecordFpsAlign = info.RecordFPSAlign,
                });
            }
            return output;
        }

        public static VideoOutputMode ToPB(this ASEva.VideoOutputMode input)
        {
            return new VideoOutputMode
            {
                OutputCodec = input.OutputCodec.ToString(),
                Size = input.Size.ToPB(),
            };
        }

        public static GetVideoDevicesOutput ToPB(this Dictionary<ASEva.VideoDeviceID, ASEva.VideoDeviceInfo> input)
        {
            var output = new GetVideoDevicesOutput();
            foreach (var inputPair in input)
            {
                if (inputPair.Key.Type == null || inputPair.Key.LocalID == null) continue;
                var outputPair = new GetVideoDevicesOutput.Types.Pair();
                outputPair.Key = new VideoDeviceID
                {
                    Type = inputPair.Key.Type,
                    LocalId = inputPair.Key.LocalID,
                };
                outputPair.Value = new VideoDeviceInfo();
                if (inputPair.Value.HardwareInfo != null) outputPair.Value.HardwareInfo = inputPair.Value.HardwareInfo;
                if (inputPair.Value.InputModes != null)
                {
                    foreach (var mode in inputPair.Value.InputModes)
                    {
                        outputPair.Value.InputModes.Add(mode.ToPB());
                    }
                }
                if (inputPair.Value.OutputModes != null)
                {
                    foreach (var mode in inputPair.Value.OutputModes)
                    {
                        outputPair.Value.OutputModes.Add(mode.ToPB());
                    }
                }
                output.InfoMap.Add(outputPair);
            }
            return output;
        }

        public static CommonImage ToPB(this ASEva.Samples.CommonImage input)
        {
            var output = new CommonImage
            {
                Width = input.Width,
                Height = input.Height,
                WithAlpha = input.WithAlpha,
                BgrInverted = input.BgrInverted,
                RowBytes = input.RowBytes,
            };
            output.Data = ByteString.CopyFrom(input.Data);
            return output;
        }

        public static (int, double, double, String, ASEva.IntRect?, bool) ToCommon(this GetVideoFrameImageInput input)
        {
            var clip = input.Clip == null ? null : (ASEva.IntRect?)new ASEva.IntRect(input.Clip.X, input.Clip.Y, input.Clip.Width, input.Clip.Height);
            return (input.Channel, input.Timeline, input.MaxGap, input.Mode, clip, input.WithAlpha);
        }

        public static GetVideoFrameImageOutput ToPB(this (ASEva.Samples.CommonImage, ASEva.Timestamp?, ASEva.Samples.CameraInfo) input)
        {
            var output = new GetVideoFrameImageOutput();
            if (input.Item1 != null) output.Item1 = input.Item1.ToPB();
            if (input.Item2 != null) output.Item2 = input.Item2.Value.ToPB();
            if (input.Item3 != null) output.Item3 = input.Item3.ToPB();
            return output;
        }

        public static (int, double, double, bool) ToCommon(this GetVideoFrameThumbnailInput input)
        {
            return (input.Channel, input.Timeline, input.MaxGap, input.WithAlpha);
        }

        public static (int, double) ToCommon(this IntAndDouble input)
        {
            return (input.Item1, input.Item2);
        }

        public static StringAndBool ToPB(this (String, bool) input)
        {
            return new StringAndBool
            {
                Item1 = input.Item1 ?? "",
                Item2 = input.Item2,
            };
        }

        public static ASEva.Samples.BusMessageSample ToCommon(this BusMessageSample input)
        {
            ASEva.BusChannelType type = ASEva.BusChannelType.None;
            ASEva.BusRawDataState state = ASEva.BusRawDataState.Received;
            Enum.TryParse(input.Type, out type);
            Enum.TryParse(input.State, out state);
            return new ASEva.Samples.BusMessageSample
            {
                Channel = (byte)input.Channel,
                Length = (ushort)input.Length,
                ID = input.Id,
                Data = input.Data.ToByteArray(),
                Type = type,
                State = state,
                ScheduleTimeOffset = input.ScheduleTimeOffset,
                SchedulePosixTime = input.SchedulePosixTime,
            };
        }

        public static ParseBusMessageOutput ToPB(this ASEva.BusSignalValue[] input)
        {
            var output = new ParseBusMessageOutput();
            foreach (var inputVal in input)
            {
                if (inputVal.signalName == null) continue;
                var outputVal = new BusSignalValue
                {
                    SignalName = inputVal.signalName,
                };
                if (inputVal.value != null) outputVal.Value = inputVal.value.Value;
                if (inputVal.unit != null) outputVal.Unit = inputVal.unit;
                if (inputVal.enumValue != null) outputVal.EnumValue = inputVal.enumValue;
                output.Values.Add(outputVal);
            }
            return output;
        }

        public static (String, byte[]) ToCommon(this PublishDataInput input)
        {
            return (input.DataId, input.Data.ToByteArray());
        }

        public static TwoStrings ToPB(this (String, String) input)
        {
            var output = new TwoStrings();
            if (input.Item1 != null) output.Item1 = input.Item1;
            if (input.Item2 != null) output.Item2 = input.Item2;
            return output;
        }

        public static ASEva.BusMessage ToCommon(this BusMessage input)
        {
            var output = new ASEva.BusMessage
            {
                Channel = input.Channel,
                ID = input.Id,
                Data = input.Data.ToByteArray(),
            };
            if (input.HasMillisecondInterval) output.MillisecondInterval = input.MillisecondInterval;
            return output;
        }

        public static (String, double[], byte[]) ToCommon(this SendRawDataInput input)
        {
            return (input.ChannelId, input.Values.ToArray(), input.HasBinary ? input.Binary.ToByteArray() : null);
        }

        public static (int, String) ToCommon(this IntAndString input)
        {
            return (input.Item1, input.HasItem2 ? input.Item2 : null);
        }

        public static (String, double) ToCommon(this StringAndDouble input)
        {
            return (input.Item1, input.Item2);
        }

        public static (String, Dictionary<String, String>) ToCommon(this SetSessionPropertiesInput input)
        {
            return (input.Session, input.Properties.ToDictionary());
        }

        public static (bool, bool, String) ToCommon(this StartOfflineOnlineInput input)
        {
            return (input.Force, input.PreviewOnly, input.HasDirName ? input.DirName : null);
        }

        public static (bool, bool, String, ulong) ToCommon(this StartRemoteInput input)
        {
            return (input.Force, input.PreviewOnly, input.HasSessionDirName ? input.SessionDirName : null, input.StartPosixTime);
        }

        public static (String, bool, ulong) ToCommon(this StartRemoteWithControllerInput input)
        {
            return (input.ControllerName, input.PreviewOnly, input.StartPosixTime);
        }

        public static (bool, double, double?) ToCommon(this StartReplayInput input)
        {
            return (input.Force, input.StartTimeline, input.HasInterestTarget ? input.InterestTarget : null);
        }

        public static (bool, bool) ToCommon(this TwoBools input)
        {
            return (input.Item1, input.Item2);
        }

        public static (String, int, int) ToCommon(this SubscribeDataInput input)
        {
            return (input.DataId, input.BufferLength, input.Timeout);
        }

        public static (String, String, int) ToCommon(this SwitchAppModeInput input)
        {
            return (input.ControllerName, input.Mode.ToString(), input.WaitSecond);
        }

        public static OptionalULong ToPB(this ulong? input)
        {
            var output = new OptionalULong();
            if (input != null) output.Value = input.Value;
            return output;
        }

        public static GPUDecoderTestResults ToPB(this ASEva.GPUDecoderTestResults input)
        {
            var output = new GPUDecoderTestResults();
            foreach (var pair in input.TestDataAvailable)
            {
                output.TestDataAvailable[pair.Key] = pair.Value;
            }
            foreach (var pair in input.TestResults)
            {
                output.TestResults[pair.Key] = pair.Value.ToPB();
            }
            return output;
        }

        public static (String, String, String) ToCommon(this ThreeStrings input)
        {
            return (input.HasItem1 ? input.Item1 : null,
                input.HasItem2 ? input.Item2 : null,
                input.HasItem3 ? input.Item3 : null);
        }

        public static (String, String, byte[]) ToCommon(this CallNativeFunctionInput input)
        {
            return (input.NativeClassId, input.FuncId, input.Input.ToByteArray());
        }

        public static DataReferenceInfo ToPB(this ASEva.DataReferenceInfo input)
        {
            var output = new DataReferenceInfo
            {
                AlwaysTransferBusMessage = input.AlwaysTransferBusMessage,
                AlwaysTransferPointCloud = input.AlwaysTransferPointCloud,
                AlwaysTransferProcessorVideo = input.AlwaysTransferProcessorVideo,
                AlwaysTransferSignal = input.AlwaysTransferSignal,
            };
            output.BusMessageReferences.AddRange(input.BusMessageReferences);
            output.PointCloudReferences.AddRange(input.PointCloudReferences);
            output.ProcessorVideoReferences.AddRange(input.ProcessorVideoReferences);
            foreach (var pair in input.SignalReferences) output.SignalReferences[pair.Key] = pair.Value;
            foreach (var pair in input.SubscriberReferences ?? []) output.SubscriberReferences[pair.Key] = pair.Value;
            return output;
        }

        public static (String, Dictionary<String, double>, double, uint?) ToCommon(this GenerateBusMessageInput input)
        {
            return (input.MessageId, input.SignalValues.ToDictionary(), input.DefaultValue, input.HasInterval ? input.Interval : null);
        }

        public static BusMessage ToPB(this ASEva.BusMessage input)
        {
            var output = new BusMessage
            {
                Channel = input.Channel,
                Id = input.ID,
                Data = ByteString.CopyFrom(input.Data),
            };
            if (input.MillisecondInterval != null) output.MillisecondInterval = input.MillisecondInterval.Value;
            return output;
        }
    }
}